<template>
    <div class="elResetBody">
        <el-button size="small" @click="exportToExcel">导出</el-button>
    </div>
</template>
  
<script setup lang="ts">
import * as XLSX from 'xlsx';
import xlsxStyle from 'xlsx-style-ts';
import saveAs from 'file-saver';
import { defineProps } from 'vue';

interface Header {
    label: string;
    key: string;
}
const props = defineProps({
    data: {
        type: Array,
        required: true
    },
    filename: {
        type: String,
        default: 'export.xlsx'
    },
    sheetName: {
        type: String,
        default: 'Sheet1'
    },
    headers: {
        type: Array,
        default: () => []
    },
    styles: {
        type: Object,
        default: () => ({})
    }
});

const applyStyles = (worksheet: XLSX.WorkSheet, headers: any[], styles: any) => {    
    // 应用表头样式
    if (headers.length > 0) {        
        headers.forEach((_header,index) => {            
            const cellRef = XLSX.utils.encode_cell({ c: index, r: 0 }); // 表头位于第一行
            if (worksheet[cellRef]) {                
                worksheet[cellRef].s = styles.header;
            }
        });
    }

    // 应用单元格样式
    for (const cell in worksheet) {
        if (cell.startsWith('!') || cell.endsWith('1')) continue; // 忽略非单元格和已经设置过的表头单元格
        worksheet[cell].s = styles.cell;
    }
    //应用个性化列宽
    if(styles.colWidths){        
        worksheet['!cols'] = styles.colWidths;
    }

};


const exportToExcel = () => {
    // console.log(props.styles);
    // 创建新的工作簿
    const workbook = XLSX.utils.book_new();
    // 处理表头，利用数组的map方法获取所有的标签（label）
    const headerInfo = (props.headers as Header[]).map(header => header.label);
    // 创建带表头的工作表
    const worksheet = XLSX.utils.aoa_to_sheet([headerInfo]);
    // 将数据追加到工作表
    XLSX.utils.sheet_add_json(worksheet, props.data, {
        skipHeader: true, // 跳过已有的表头行
        origin: -1, // 从工作表的末尾追加
        header: (props.headers as Header[]).map(header => header.key)
    });    
    // 应用样式
    applyStyles(worksheet, props.headers, props.styles);    
    // 将工作表添加到工作簿
    XLSX.utils.book_append_sheet(workbook, worksheet, props.sheetName);   
    // 生成 Excel 文件并触发下载
    // XLSX.writeFile(workbook, props.filename);
    // 生成 Excel 文件的 Blob    
    const wbout = xlsxStyle.write(workbook, { bookType: "xlsx", type: "binary" });
    // 将字符串转换为ArrayBuffer
    function s2ab(s: string) {
        const buf = new ArrayBuffer(s.length);
        const view = new Uint8Array(buf);
        for (let i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
        return buf;
    }
    // 利用Blob对象触发下载
    saveAs(new Blob([s2ab(wbout)], { type: "application/octet-stream" }), props.filename);
};
</script>